%%%-------------------------------------------------------------------
%%% @author wukai
%%% @copyright (C) 2018, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 13. 一月 2018 15:59
%%%-------------------------------------------------------------------
-module(himq_admin).
-include("himq.hrl").
-include_lib("stdlib/include/qlc.hrl").
-author("wukai").
-behaviour(cowboy_middleware).

%%token过期时间

%% API
-export([init/2]).
-export([handler/3]).
-export([execute/2]).
-export([start_admin/0]).
-export([msg/2, msg/3]).
-define(MESSAGE_GET_SUCCESS, <<"获取成功"/utf8>>).
-define(MESSAGE_GET_FAILED, <<"获取失败"/utf8>>).
-define(MESSAGE_SAVE_SUCCESS, <<"保存成功"/utf8>>).
-define(MESSAGE_SAVE_FAILED, <<"保存失败"/utf8>>).

-define(PATH_MQTT_USER, <<"/admin/api/mqtt/user">>).
%%disconnect
-define(PATH_MQTT_DISCONNECT, <<"/admin/api/mqtt/disconnect">>).
-define(PATH_MQTT_USER_DELETE, <<"/admin/api/mqtt/user/delete">>).
-define(PATH_MQTT_SESSION_LIST, <<"/admin/api/mqtt/session/list">>).
-define(PATH_MQTT_LINK_LOG, <<"/admin/api/mqtt/link_log">>).
-define(PATH_MQTT_RETAIN, <<"/admin/api/mqtt/retain">>).
-define(PATH_MQTT_UNDELIVERED, <<"/admin/api/mqtt/undelivered">>).
-define(PATH_ADMIN_SYS_INFO, <<"/admin/api/sysinfo">>).
-define(PATH_ADMIN_APP, <<"/admin/api/app">>).
-define(PATH_ADMIN_APP_DELETE, <<"/admin/api/app/delete">>).

-define(PATH_ADMIN_SUBSCRIBE, <<"/admin/api/subscribe">>).
-define(PATH_ADMIN_DEVICE_DELETE, <<"/admin/api/device/delete">>).
-define(PATH_ADMIN_DEVICE, <<"/admin/api/device">>).

-define(PATH_HIMQ_PUSH, <<"/himq/api/push">>).
-define(PATH_HIMQ_PUSH_LIST, <<"/admin/api/push/list">>).
-define(PATH_HIMQ_PUSH_DELETE, <<"/admin/api/push/delete">>).

-define(PATH_HIMQ_SETTING_BASE, <<"/admin/api/setting/base">>).
-define(PATH_HIMQ_SETTING_PUSH, <<"/admin/api/setting/push">>).

-define(PATH_HIMQ_COMMAND, <<"/admin/api/command">>).

-define(HTTP_POST, <<"POST">>).
-define(HTTP_GET, <<"GET">>).

execute(Req, Env) ->
  Path = cowboy_req:path(Req),
  lager:info("request path:~p", [Path]),
  auth(Path, Req, Env).

auth(<<"/admin/api/login">>, Req, Env) -> {ok, Req, Env};
auth(<<"/himq/api", _Left/binary>>, Req, Env) ->
  %%lager:info("encode body-->~p",[cowboy_req:read_and_match_urlencoded_body([],Req)]),
  %%{ok,#{appid:=AppKey},_}= cowboy_req:read_and_match_urlencoded_body([appid],Req),
  AppKey = cowboy_req:header(<<"appid">>, Req),
  lager:info("appid from head:~p", [AppKey]),
  %%AppKey = proplists:get_value(<<"appid">>, PostVals, undefined),

  if
    AppKey =:= undefined ->
      Data = jsx:encode(msg(1, <<"非法调用AppId不能为空"/utf8>>)),
      Req1 = cowboy_req:reply(200, #{}, Data, Req),
      {stop, Req1};
    true ->
      case himq_mnesia:record_exist(?TABLE_APPLICATION, binary_to_list(AppKey)) of
        true -> {ok, Req, Env};
        false ->
          Data = jsx:encode(msg(1, <<"非法调用Appkey不存在"/utf8>>)),
          Req1 = cowboy_req:reply(200, #{}, Data, Req),
          {stop, Req1}
      end
  end;

auth(<<"/admin/api", _Left/binary>>, Req, Env) ->
%%  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
%%  lager:info("encode body-->~p",[PostVals]),
  Token = cowboy_req:header(<<"token">>, Req),
  lager:info("token:~p", [Token]),
  %%获取token和username
  %lager:info("claims:~p", [jwt:decode(Token, ?TOKEN_KEY)]),

  {ok, #{<<"user_name">> := Username}} = jwt:decode(Token, ?TOKEN_KEY),
  %%判断token是否合法
  case himq_mnesia:token_exist(Username, Token) of
    true ->
      NReq = cowboy_req:set_resp_header(<<"username">>, Username, Req),
      {ok, NReq, Env};
    false ->
      Data = jsx:encode(msg(1, <<"token已过期"/utf8>>)),
      Req0 = cowboy_req:reply(401, #{}, Data, Req),
      {stop, Req0}
  end.


app_exist(Name) ->
  F = fun() -> mnesia:match_object(?TABLE_APPLICATION, #himq_auth_app{name = Name}, read) end,
  case mnesia:transaction(F) of
    {atomic, List} when is_list(List), length(List) > 0 -> true;
    _ -> false
  end.

router() ->
  [
    {"/admin/api/login", himq_admin, []},
    {"/admin/api/reset/password", himq_admin, []},
    {"/admin/api/logout", himq_admin, []},
    {"/admin/api/mqtt/subscriber", himq_admin, []},
    {binary_to_list(?PATH_MQTT_USER), himq_admin, []},
    {binary_to_list(?PATH_MQTT_SESSION_LIST), himq_admin, []},
    {binary_to_list(?PATH_MQTT_USER_DELETE), himq_admin, []},
    {binary_to_list(?PATH_ADMIN_SYS_INFO), himq_admin, []},
    {binary_to_list(?PATH_MQTT_LINK_LOG), himq_admin, []},

    {binary_to_list(?PATH_ADMIN_APP), himq_admin, []},
    {binary_to_list(?PATH_ADMIN_APP_DELETE), himq_admin, []},

    {binary_to_list(?PATH_ADMIN_DEVICE_DELETE), himq_admin, []},
    {binary_to_list(?PATH_ADMIN_DEVICE), himq_admin, []},

    {binary_to_list(?PATH_MQTT_RETAIN), himq_admin, []},
    {binary_to_list(?PATH_MQTT_UNDELIVERED), himq_admin, []},
    {binary_to_list(?PATH_ADMIN_SUBSCRIBE), himq_admin, []},
    {binary_to_list(?PATH_HIMQ_PUSH), himq_admin, []},
    {binary_to_list(?PATH_HIMQ_PUSH_LIST), himq_admin, []},
    {binary_to_list(?PATH_HIMQ_PUSH_DELETE), himq_admin, []},
    {binary_to_list(?PATH_HIMQ_COMMAND), himq_admin, []},
    {binary_to_list(?PATH_MQTT_DISCONNECT), himq_admin, []},
    {binary_to_list(?PATH_HIMQ_SETTING_BASE), himq_admin, []},
    {binary_to_list(?PATH_HIMQ_SETTING_PUSH), himq_admin, []},

    {"/mqtt", himq_mqtt_ws, []}
  ].
start_admin() ->
  Dispatch = cowboy_router:compile([{'_', router()}]),
  {ok, Ip} = inet:parse_address(himq_eenv:get(?HIMQ, ?HIMQ_LISTENER_HTTP_IP, "0.0.0.0")),
  Port = himq_eenv:get(?HIMQ, ?HIMQ_LISTENER_HTTP_PORT, 9999),
  cowboy:start_clear(himq_admin, [{ip, Ip}, {port, Port}], #{
    env => #{dispatch => Dispatch},
    middlewares => [cowboy_router, himq_admin, cowboy_handler]
  }).

msg(Code, Message, Data) ->
  #{<<"code">>=>Code, <<"message">>=>Message, <<"data">>=>Data}.
msg(Code, Message) ->
  msg(Code, Message, <<"">>).

admin_login(Username, Password) ->
  case himq_mnesia:auth_admin(Username, Password) of
    true ->
      Claims = [{user_name, Username}],
      {ok, Token} = jwt:encode(<<"HS256">>, Claims, ?TOKEN_KEY),
      ExpireTime = himq:timestamp() + ?TOKEN_EXPIRE_TIME,
      Item = #himq_admin{username = Username, password = himq:to_binary(himq:md5(Password)), token = Token, expire = ExpireTime},
      case himq_mnesia:write(Item) of
        {atomic, ok} ->
          msg(0, <<"登录成功"/utf8>>, #{token=>Token});
        Err ->
          lager:info("login error ~p", [Err]),
          msg(1, <<"登录失败"/utf8>>)
      end;
    false ->

      msg(1, <<"登录失败"/utf8>>)
  end.

%%重置密码
admin_reset(Username, NewPassword, OldPassword) ->
  NPass = himq:to_binary(himq:md5(NewPassword)),
  OPass = himq:to_binary(himq:md5(OldPassword)),
  lager:info("reset info:~p", [NPass, Username, OPass]),
  Fun = fun() ->
    [Admin] = mnesia:read({?TABLE_ADMIN, Username}),
    mnesia:write(Admin#himq_admin{password = NPass})
        end,
  case mnesia:transaction(Fun) of
    {atomic, ok} -> msg(0, <<"密码修改成功"/utf8>>, <<"">>);
    _ -> msg(1, <<"密码修改失败"/utf8>>, <<"">>)
  end.

init(Req, State) ->
  Path = cowboy_req:path(Req),
  Data = handler(cowboy_req:method(Req), Path, Req),
  NReq = cowboy_req:reply(200, #{
    <<"content-type">> => <<"text/plain; charset=utf-8">>,
    <<"Access-Control-Allow-Origin">> => <<"*">>
  }, Data, Req),
  {ok, NReq, State}.


%%管理员登录
handler(?HTTP_POST, <<"/admin/api/login">>, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Password = proplists:get_value(<<"password">>, PostVals),
  Username = proplists:get_value(<<"username">>, PostVals),
  jsx:encode(admin_login(Username, Password));

%%退出登录
handler(?HTTP_POST, <<"/admin/api/logout">>, Req) ->
  Username = cowboy_req:resp_header(<<"username">>, Req),
  Query = "update himq_admin set token= ? where username=?",
  case himq_mysql:query(?HIMQ, Query, [<<"">>, Username]) of
    ok -> jsx:encode(msg(0, <<"退出成功"/utf8>>));
    _ -> jsx:encode(msg(1, <<"退出失败"/utf8>>))
  end;

%%重置密码
handler(?HTTP_POST, <<"/admin/api/reset/password">>, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  OldPassword = proplists:get_value(<<"old_password">>, PostVals),
  NewPassword = proplists:get_value(<<"password">>, PostVals),
  RePassword = proplists:get_value(<<"re_password">>, PostVals),
  if
    NewPassword =:= RePassword ->
      Username = cowboy_req:resp_header(<<"username">>, Req),
      case himq_mnesia:auth_admin(Username, OldPassword) of
        true ->
          jsx:encode(admin_reset(Username, NewPassword, OldPassword));
        false ->
          jsx:encode(msg(1, <<"原始密码错误"/utf8>>))
      end;
    true ->
      jsx:encode(msg(1, <<"两次密码不一致"/utf8>>))
  end;


handler(?HTTP_POST, ?PATH_HIMQ_SETTING_BASE, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  AuthWay = proplists:get_value(<<"auth_way">>, PostVals),
  Publish = proplists:get_value(<<"offline[0]">>, PostVals, 0),
  Save = proplists:get_value(<<"offline[1]">>, PostVals, 0),
  Actions = lists:filter(fun(E) -> E =/= 0 end, [Publish, Save]),
  lager:info("auth_way ~p offline:~p,", [AuthWay, Actions]),
  Item = #himq_setting{key = ?SETTING_BASE_KEY, value = #{auth_way=>AuthWay, offline=>Actions}},
  case himq_mnesia:write(Item) of
    {atomic, ok} ->
      himq_mnesia:eenv_setting(),
      jsx:encode(msg(0, ?MESSAGE_SAVE_SUCCESS));
    Err ->
      lager:error("save base_setting item error :~p", [Err]),
      jsx:encode(msg(1, ?MESSAGE_SAVE_FAILED))
  end;

handler(?HTTP_GET, ?PATH_HIMQ_SETTING_BASE, _Req) ->
  case himq_mnesia:read(?TABLE_SETTING, ?SETTING_BASE_KEY) of
    {atomic, [Item]} ->
      jsx:encode(msg(0, ?MESSAGE_GET_SUCCESS, Item#himq_setting.value));
    Err ->
      lager:error("get base_setting item error :~p", [Err]),
      jsx:encode(msg(1, ?MESSAGE_GET_FAILED))
  end;

handler(?HTTP_POST, ?PATH_HIMQ_SETTING_PUSH, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Qos = proplists:get_value(<<"push_qos">>, PostVals),
  Save = proplists:get_value(<<"push_save">>, PostVals),
  Item = #himq_setting{key = ?SETTING_PUSH_KEY, value = #{push_qos=>Qos, push_save=>Save}},
  case himq_mnesia:write(Item) of
    {atomic, ok} ->
      himq_mnesia:eenv_setting(),
      jsx:encode(msg(0, ?MESSAGE_SAVE_SUCCESS));
    Err ->
      lager:error("save base_setting item error :~p", [Err]),
      jsx:encode(msg(1, ?MESSAGE_SAVE_FAILED))
  end;

handler(?HTTP_GET, ?PATH_HIMQ_SETTING_PUSH, _Req) ->
  case himq_mnesia:read(?TABLE_SETTING, ?SETTING_PUSH_KEY) of
    {atomic, [Item]} ->
      jsx:encode(msg(0, ?MESSAGE_GET_SUCCESS, Item#himq_setting.value));
    Err ->
      lager:error("get base_setting item error :~p", [Err]),
      jsx:encode(msg(1, ?MESSAGE_GET_FAILED))
  end;

handler(?HTTP_POST, ?PATH_MQTT_USER, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Creator = cowboy_req:resp_header(<<"username">>, Req),
  Username = proplists:get_value(<<"username">>, PostVals),
  Password = proplists:get_value(<<"password">>, PostVals),
  Remark = proplists:get_value(<<"remark">>, PostVals),
  Item = #himq_mqtt_user{username = Username, password = Password, creator = Creator, updated_at = himq:timestamp(), created_at = himq:timestamp(), remark = Remark},
  case mnesia:transaction(fun() ->
    mnesia:write(Item)
                          end) of
    {atomic, ok} ->
      himq_mnesia:eenv_users(),
      jsx:encode(msg(0, <<"添加成功"/utf8>>));
    Err ->
      lager:info("add mqtt auth user error ~p", [Err]),
      jsx:encode(msg(1, <<"添加失败"/utf8>>))
  end;

handler(?HTTP_POST, ?PATH_MQTT_USER_DELETE, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Username = proplists:get_value(<<"username">>, PostVals),
  case mnesia:transaction(fun() -> mnesia:delete({?TABLE_MQTT_USER, Username}) end) of
    {atomic, ok} ->
      jsx:encode(msg(0, <<"删除成功"/utf8>>));
    _ ->
      jsx:encode(msg(1, <<"删除失败"/utf8>>))
  end;

handler(?HTTP_GET, ?PATH_MQTT_USER, Req) ->
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  Fun = fun(E) ->
    #{username=>E#himq_mqtt_user.username, password=>E#himq_mqtt_user.password,
      creator=>E#himq_mqtt_user.creator, remark=>E#himq_mqtt_user.remark,
      created_at=>E#himq_mqtt_user.created_at, updated_at=>E#himq_mqtt_user.updated_at}
        end,
  Data = himq_mnesia:page(?TABLE_MQTT_USER, binary_to_integer(Page), binary_to_integer(PageSize), Fun),
  jsx:encode(msg(0, <<"授权用户列表"/utf8>>, Data));

handler(?HTTP_POST, ?PATH_ADMIN_DEVICE, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  lager:info("params:~p", [PostVals]),
  Creator = cowboy_req:resp_header(<<"username">>, Req),
  User = proplists:get_value(<<"user">>, PostVals),
  Id = proplists:get_value(<<"client">>, PostVals),
  Remark = proplists:get_value(<<"remark">>, PostVals),

  case himq_mnesia:record_exist(?TABLE_DEVICE, Id) of
    true ->
      jsx:encode(msg(1, <<"设备已存在"/utf8>>));
    false ->
      Item = #himq_device{id = Id,
        creator = Creator,
        user = User,
        remark = Remark,
        created_at = himq:timestamp(),
        updated_at = himq:timestamp()},

      case himq_mnesia:write(Item) of
        {atomic, ok} ->
          jsx:encode(msg(0, <<"添加成功"/utf8>>));
        _ ->
          jsx:encode(msg(1, <<"添加失败"/utf8>>))
      end
  end;

handler(?HTTP_POST, ?PATH_ADMIN_DEVICE_DELETE, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Id = proplists:get_value(<<"id">>, PostVals),

  case himq_mnesia:delete(?TABLE_DEVICE, Id) of
    {atomic, ok} ->
      jsx:encode(msg(0, <<"删除设备成功"/utf8>>));
    _ ->
      jsx:encode(msg(1, <<"删除失败"/utf8>>))
  end;

handler(?HTTP_GET, ?PATH_ADMIN_DEVICE, Req) ->
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  %%select id,name,content from users where id>100073 order by id asc limit 20
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  Fun = fun(E) ->
    #{id=>E#himq_device.id, client=>E#himq_device.id, user=>E#himq_device.user, remark=>E#himq_device.remark,
      created_at=>E#himq_device.created_at, updated_at=>E#himq_device.updated_at}
        end,
  Data = himq_mnesia:page(?TABLE_DEVICE, binary_to_integer(Page), binary_to_integer(PageSize), Fun),
  jsx:encode(msg(0, <<"数据列表"/utf8>>, Data));

%%获取会话列表
handler(?HTTP_GET, ?PATH_MQTT_SESSION_LIST, Req) ->
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  lager:info("page:~p,page_size:~p", [Page, PageSize]),
  jsx:encode(msg(0, <<"会话列表"/utf8>>, himq_session:session_list(
    binary_to_integer(Page),
    binary_to_integer(PageSize))));

%%获取订阅列表
handler(?HTTP_GET, ?PATH_ADMIN_SUBSCRIBE, Req) ->
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  lager:info("page:~p,page_size:~p", [Page, PageSize]),
  jsx:encode(msg(0, <<"订阅列表"/utf8>>, himq_mqtt_router:subscribe_list(
    binary_to_integer(Page),
    binary_to_integer(PageSize))));


handler(?HTTP_GET, ?PATH_ADMIN_SYS_INFO, _Req) ->
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  jsx:encode(msg(0, <<"系统信息"/utf8>>, sys_info()));

handler(?HTTP_GET, ?PATH_ADMIN_APP, Req) ->
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  QH = qlc:q([Item || Item <- mnesia:table(?TABLE_APPLICATION), Item#himq_auth_app.enable =:= true]),
  Data = himq_mnesia:page_where(?TABLE_APPLICATION, binary_to_integer(Page), binary_to_integer(PageSize), fun(E) ->
    #{app_name=>E#himq_auth_app.name,
      app_key=>list_to_binary(E#himq_auth_app.key),
      remark=>E#himq_auth_app.remark,
      enable=>E#himq_auth_app.enable,
      creator=>E#himq_auth_app.creator,
      created_at=>E#himq_auth_app.create_at,
      updated_at=>E#himq_auth_app.updated_at} end, QH),
  jsx:encode(msg(0, <<"应用列表"/utf8>>, Data));

handler(?HTTP_GET, ?PATH_MQTT_LINK_LOG, Req) ->

  %%select id,name,content from users where id>100073 order by id asc limit 20
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  Fun = fun(Item) ->
    #{id=>Item#himq_link.id, protocol=>Item#himq_link.protocol, status=>Item#himq_link.status,
      created_at=>Item#himq_link.created_at, remark=>Item#himq_link.remark}
        end,
  Data = himq_mnesia:page(?TABLE_LINK, binary_to_integer(Page), binary_to_integer(PageSize), Fun),
  jsx:encode(msg(0, <<"数据列表"/utf8>>, Data));

%%保留消息
handler(?HTTP_GET, ?PATH_MQTT_RETAIN, Req) ->
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  %%select id,name,content from users where id>100073 order by id asc limit 20
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  Fun = fun(Item) ->
    #{topic=>Item#himq_retain.topic, client=>list_to_binary(Item#himq_retain.id), qos=>Item#himq_retain.qos, packet=>Item#himq_retain.packet,
      created_at=>Item#himq_retain.created_at, updated_at=>Item#himq_retain.updated_at}
        end,
  Data = himq_mnesia:page(?TABLE_RETAIN, binary_to_integer(Page), binary_to_integer(PageSize), Fun),
  jsx:encode(msg(0, <<"数据列表"/utf8>>, Data));
%%未发送消息
handler(?HTTP_GET, ?PATH_MQTT_UNDELIVERED, Req) ->
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  %%select id,name,content from users where id>100073 order by id asc limit 20
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  Fun = fun(Item) ->
    #{topic=>Item#himq_undelivered.topic,
      from=>himq:to_binary(Item#himq_undelivered.from),
      qos=>Item#himq_undelivered.qos,
      id=>himq:to_binary(Item#himq_undelivered.uuid),
      client=>himq:to_binary(Item#himq_undelivered.id),
      retain=>Item#himq_undelivered.retain,
      dup=>Item#himq_undelivered.dup,
      packet=>Item#himq_undelivered.packet, remark=>Item#himq_undelivered.remark, step=>Item#himq_undelivered.step,
      created_at=>Item#himq_undelivered.created_at}
        end,
  Data = himq_mnesia:page(?TABLE_UNDELIVERED, binary_to_integer(Page), binary_to_integer(PageSize), Fun),
  jsx:encode(msg(0, <<"数据列表"/utf8>>, Data));

handler(?HTTP_POST, ?PATH_ADMIN_APP, Req) ->
  Username = cowboy_req:resp_header(<<"username">>, Req),
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  AppName = proplists:get_value(<<"name">>, PostVals),
  Remark = proplists:get_value(<<"remark">>, PostVals),
  %lager:info("appname:~p,remark:~p", [AppName, Remark]),

  case app_exist(AppName) of
    true -> jsx:encode(msg(1, <<"应用名称已存在"/utf8>>));
    false ->
      AppKey = himq:md5(AppName),
      App = #himq_auth_app{
        key = AppKey,
        remark = Remark,
        create_at = himq:timestamp(),
        updated_at = himq:timestamp(),
        creator = Username,
        enable = true,
        name = AppName},
      case himq_mnesia:write(App) of
        {atomic, ok} ->
          jsx:encode(msg(0, <<"添加应用成功"/utf8>>));
        Err ->
          lager:info("save app data error  ~p", [Err]),
          jsx:encode(msg(1, <<"添加应用失败"/utf8>>))
      end
  end;
handler(?HTTP_POST, ?PATH_ADMIN_APP_DELETE, Req) ->
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  AppKey = proplists:get_value(<<"app_key">>, PostVals),
  case himq_mnesia:delete(?TABLE_APPLICATION, binary_to_list(AppKey)) of
    {atomic, ok} ->
      jsx:encode(msg(0, <<"删除应用成功"/utf8>>));
    Err ->
      lager:info("delete app error:~p", [Err]),
      jsx:encode(msg(1, <<"删除应用失败"/utf8>>))
  end;

handler(?HTTP_POST, ?PATH_HIMQ_PUSH, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  AppId = proplists:get_value(<<"appid">>, PostVals),
  Client = proplists:get_value(<<"client">>, PostVals),
  CommandId = proplists:get_value(<<"command">>, PostVals),
  Qos = proplists:get_value(<<"qos">>, PostVals),
  Retain = proplists:get_value(<<"retain">>, PostVals),
  lager:info("push params app:~p client:~p,command:~p", [AppId, Client, CommandId]),
  %%{id,name,packet,topic,qos,retain,push_time,status,end_time}).
  case mnesia:transaction(fun() -> mnesia:read({?TABLE_COMMAND, binary_to_integer(CommandId)}) end) of
    {atomic, [Command]} ->
      MsgId = himq:md5(lists:concat(erlang:tuple_to_list(erlang:timestamp()) ++ erlang:pid_to_list(self()))),
      Push = #himq_push{
        id = MsgId,
        client = Client,
        name = Command#himq_command.name,
        packet = Command#himq_command.packet,
        topic = Command#himq_command.topic,
        qos = binary_to_integer(Qos),
        retain = binary_to_integer(Retain),
        created_at = himq:timestamp(),
        status = record,
        end_time = 0,
        app = AppId},
      case himq_mnesia:write(Push) of
        {atomic, ok} ->
          Data = himq_session:push(Push),
          jsx:encode(Data);
        Err ->
          lager:info("save push msg faied reason ~p", [Err]),
          jsx:encode(msg(1, <<"推送失败"/utf8>>))
      end;
    Err ->
      lager:info("command not found ~p", [Err]),
      jsx:encode(msg(1, <<"指令不存在"/utf8>>))
  end;

handler(?HTTP_POST, ?PATH_MQTT_DISCONNECT, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Client = proplists:get_value(<<"client">>, PostVals),
  case ets:lookup(?SESSION_TAB, binary_to_list(Client)) of
    [] ->
      jsx:encode(msg(1, <<"设备已离线"/utf8>>));
    [S | _Tail] ->
      Pid = S#himq_session.send_pid,
      case is_process_alive(Pid) of
        true ->
          Pid ! {remove, "admin disconnect"},
          jsx:encode(msg(0, <<"消息已发送"/utf8>>));
        fase ->
          jsx:encode(msg(1, <<"设备已离线"/utf8>>))
      end
  end;

handler(?HTTP_GET, ?PATH_HIMQ_PUSH_LIST, Req) ->
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  %%select id,name,content from users where id>100073 order by id asc limit 20
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  Fun = fun(Item) ->
    #{topic=>Item#himq_push.topic,
      qos=>Item#himq_push.qos,
      client=>Item#himq_push.client,
      id=>list_to_binary(Item#himq_push.id),
      status=>Item#himq_push.status,
      retain=>Item#himq_push.retain,
      packet=>Item#himq_push.packet,
      name=>Item#himq_push.name,
      created_at=>Item#himq_push.created_at,
      updated_at=>Item#himq_push.end_time}
        end,

  Data = himq_mnesia:page(?TABLE_PUSH, binary_to_integer(Page), binary_to_integer(PageSize), Fun),
  jsx:encode(msg(0, <<"数据列表"/utf8>>, Data));

handler(?HTTP_POST, ?PATH_HIMQ_PUSH_DELETE, Req) ->
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Id = proplists:get_value(<<"id">>, PostVals),
  case himq_mnesia:delete(?TABLE_PUSH, binary_to_list(Id)) of
    {atomic, ok} ->
      jsx:encode(msg(0, <<"删除成功"/utf8>>));
    _ ->
      jsx:encode(msg(1, <<"删除失败"/utf8>>))
  end;

handler(?HTTP_POST, ?PATH_HIMQ_COMMAND, Req) ->
  %%lager:info("page:~p,page_size:~p",[Page,PageSize]),
  {ok, PostVals, _Req0} = cowboy_req:read_urlencoded_body(Req),
  Name = proplists:get_value(<<"name">>, PostVals),
  Packet = proplists:get_value(<<"packet">>, PostVals),
  Type = proplists:get_value(<<"type">>, PostVals),
  Topic = proplists:get_value(<<"topic">>, PostVals),
  Id = himq_mnesia:sequence(?TABLE_SEQUENCE),
  AddTime = himq:timestamp(),
  Item = #himq_command{id = Id, topic = Topic, created_at = AddTime, name = Name, packet = Packet, type = Type},
  case himq_mnesia:write(Item) of
    {atomic, ok} ->
      jsx:encode(msg(0, <<"保存成功"/utf8>>));
    Err ->
      lager:info("save command data  ~p", [Err]),
      jsx:encode(msg(1, <<"保存失败"/utf8>>))
  end;

handler(?HTTP_GET, ?PATH_HIMQ_COMMAND, Req) ->
  #{page_size:=PageSize, page:=Page} = cowboy_req:match_qs([page_size, page], Req),
  lager:info("page:~p,page_size:~p", [Page, PageSize]),
  Data = himq_mnesia:page(?TABLE_COMMAND, binary_to_integer(Page), binary_to_integer(PageSize), fun(E) ->
    #{name=>E#himq_command.name,
      packet=>E#himq_command.packet,
      topic=>E#himq_command.topic,
      type=>E#himq_command.type,
      created_at=>E#himq_command.created_at,
      id=>E#himq_command.id} end
  ),
  jsx:encode(msg(0, <<"指令列表"/utf8>>, Data));


handler(_, _Path, Req) -> cowboy_req:reply(404, Req).


sys_info() ->
  SysInfo = observer_backend:sys_info(),
  {Sys, Memory, Stat, Limit} = info_fields(),
  I = fill_info(Sys, SysInfo),
  M = fill_info(Memory, SysInfo),
  S = fill_info(Stat, SysInfo),
  L = fill_info(Limit, SysInfo),
  [I, M, S, L].

info_fields() ->
  SysInfo =
    {"sys_arch",
      [
        {"otp_release", otp_release},
        {"version", version},
        {"os", system_architecture},
        {"process_limit", process_limit},
        {"smp", smp_support},
        {"schedulers", schedulers},
        {"schedulers_available", schedulers_available},
        {"logical_processors", logical_processors}
      ]},

  Memory =
    {"memory_use",
      [
        {"total", total},
        {"processes", processes},
        {"atoms", atom},
        {"binaries", binary},
        {"ets", ets}
      ]},
  Statistics = {"statistics",
    [
      {"up_time", uptime},
      {"process_count", process_count},
      {"run_queue", run_queue},
      {"io_input", io_input},
      {"io_output", io_output}
    ]},

  Limit = {"limit",
    [
      {"atom_limit", atom_limit},
      {"atom_count", atom_count},
      {"process_limit", process_limit},
      {"process_count", process_count},
      {"port_limit", port_limit},
      {"port_count", port_count},
      {"ets_limit", ets_limit},
      {"ets_count", ets_count},
      {"dist_buf_busy_limit", dist_buf_busy_limit}
    ]
  },

  {SysInfo, Memory, Statistics, Limit}.

fill_info({Title, Keys}, Props) when is_list(Keys) ->
  L = get_value(Keys, [], Props),
  {himq:to_binary(Title), L}.

get_value([{KeyTitle, Key} | Rest], Acc, Props) ->
  NewACC = case proplists:get_value(Key, Props) of
             undefined -> Acc;
             V -> Acc ++ [{himq:to_binary(KeyTitle), himq:to_binary(V)}]
           end,
  get_value(Rest, NewACC, Props);

get_value([], Acc, _) -> Acc.
